<?php

declare(strict_types=1);

namespace Zlf\AppValidate\Rule;

/**
 * 元素是否在数组中验证
 *
 * 该类用于检查给定值是否不在预定义的枚举数组中。它继承自 RuleAbstract，并提供了设置枚举数据、
 * 错误提示信息、是否严格检查类型的设置以及执行具体验证逻辑的方法。
 */
class ExcludeInValidate extends RuleAbstract
{
    /**
     * 枚举数据源
     *
     * 这些值被视为无效选项。如果输入值与这些值之一匹配，则认为验证失败。
     *
     * @var array
     */
    protected $_enums = [];

    /**
     * 错误提示
     *
     * 当验证失败时显示的错误消息。可以包含占位符 `{label}`，将在实际使用时替换为字段标签。
     *
     * @var string
     */
    protected $_error = '{label}不能选择当前选项';

    /**
     * 是否还要检查类型
     *
     * 如果设置为 true，则不仅检查值的存在性，还检查其类型（严格的类型比较）。
     * 默认情况下不启用严格类型检查。
     *
     * @var bool
     */
    protected $_strict = false;

    /**
     * 设置枚举数据
     *
     * 允许调用者动态地设置或修改枚举值列表。
     *
     * @param array $enums 新的枚举值列表
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setEnums(array $enums): ExcludeInValidate
    {
        $this->_enums = $enums;
        return $this;
    }

    /**
     * 设置是否严格检查类型
     *
     * 允许调用者动态地设置或修改是否进行严格的类型检查。
     *
     * @param bool $strict 是否启用严格类型检查
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setStrict(bool $strict): ExcludeInValidate
    {
        $this->_strict = $strict;
        return $this;
    }

    /**
     * 验证输入值是否不在枚举数组中
     *
     * 如果输入值是一个字符串或数值类型：
     * - 如果允许跳过空值并且值为空，则直接通过验证。
     * - 检查值是否存在于枚举数组中（根据严格模式），如果存在则返回错误信息。
     * - 如果值不在枚举数组中，记录该值并返回 true 表示验证通过。
     * 如果输入值不是字符串或数值类型，则添加错误信息并返回 false。
     *
     * @param mixed $value 待验证的值
     * @param string $label 字段标签，用于错误提示信息中
     * @return bool 验证结果：成功为 true，失败为 false
     */
    public function validate($value, string $label = ''): bool
    {
        if (is_string($value) || is_numeric($value)) {
            // 如果允许跳过空值并且值为空，则直接通过验证
            if (empty($value) && $this->_noSkipping === true) {
                $this->_value = $value;
                return true;
            }
            // 检查值是否存在于枚举数组中（根据严格模式）
            if (in_array($value, $this->_enums, $this->_strict)) {
                return $this->addError($this->_error, $label);
            }
            // 记录值并返回 true 表示验证通过
            $this->_value = $value;
            return true;
        }
        // 如果输入值不是字符串或数值类型，添加错误信息并返回 false
        return $this->addError($this->_error, $label);
    }
}